﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Reflection;
using System.Text;
using OpenLS.Spreadsheet.Formats.Biff.Records;

namespace OpenLS.Spreadsheet.Formats.Biff
{
#if DEBUG
    class ObjectFormatter
    {
        public static string ToString(object o, List<string>toExclude)
        {
            return (new ObjectFormatter()).ToString(o, toExclude, 0);
        }
        Dictionary<object, object> alreadyProcessed = new Dictionary<object, object>();
        string ToString(object o, List<string>toExclude, int tabs)
        {
            Contract.Assert((new StackTrace()).ToString().Length < 20000);
            //\\Debug.WriteLine(new StackTrace());
            if (toExclude == null)
                toExclude = new List<string>();
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.Append(o.GetType().Name);
            if (o is CFParsedFormulaNoCCE)
            {
                CFParsedFormulaNoCCE cf = (CFParsedFormulaNoCCE) o;
                stringBuilder.Append( " " + cf.TokenCount);
                foreach (var token in cf.GetTokens())
                    stringBuilder.Append(Environment.NewLine + ToString(token, toExclude, tabs + 1));
            }
            string tabsString = "";
            for (int i = 0; i < tabs; i++ )
            {
                tabsString += " ";
            }
                stringBuilder.Append(Environment.NewLine + tabsString +   "{");
            IEnumerable<FieldInfo> fields = o.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.FlattenHierarchy | BindingFlags.GetField);
            fields = Enumerable.Where(fields, f => !toExclude.Contains(f.Name));
            var strings = Enumerable.Select(fields, f => f.Name.Replace(">k__BackingField", "") + " = " + NiceValue(f.GetValue(o), tabs+1));
            stringBuilder.Append(string.Join(", "+Environment.NewLine + tabsString, Enumerable.ToArray(strings)));
            stringBuilder.Append(Environment.NewLine+ tabsString + "} // " + o.GetType().Name);
            return stringBuilder.ToString();
        }
        string NiceValue(object o, int tabs)
        {
            if (o == null)
                return "null";
            if (o is string)
                return o.ToString();
            if (o is Guid)
                return o.ToString();
            if (o is double)
                return o.ToString();
            if (o is float)
                return o.ToString();
            if (o is uint || o is int || o is short || o is ushort || o is byte || o is sbyte || o is bool || o is long || o is ulong)
                return o.ToString();
            if (o is Enum)
                return o.ToString();
            if (o is IEnumerable)
            {
                List<string> ss = new List<string>();
                foreach (var oo in ((IEnumerable)o))
                {
                    if (alreadyProcessed.ContainsKey(o) )
                    {
                        ss.Add(o.ToString() + " <- ");
                    }
                    else if (typeof (ValueType).IsAssignableFrom(oo.GetType()))
                    {
                        ss.Add(NiceValue(oo, tabs + 1));
                    }
                    else
                    {
                        ss.Add(NiceValue(oo, tabs + 1));
                    }
                }
                    return string.Format("{{{0}}}", string.Join(",", ss.ToArray()));
                
            }
            if (typeof (ValueType).IsAssignableFrom(o.GetType()))
                return ToString(o, null, tabs+1);
            if (alreadyProcessed.ContainsKey(o))
                return o.ToString();
            alreadyProcessed.Add(o, o);
            return ToString(o,null,  tabs + 1);
        }

    }
#endif
}